/*
 *   Email plugin for libpurple
 *   Copyright (C) 2017 Alyssa Rosenzweig
 *   Copyright (C) 2016  Eion Robb
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __GNUC__
#include <unistd.h>
#endif
#include <errno.h>

#ifdef ENABLE_NLS
#      define GETTEXT_PACKAGE "purple-email"
#      include <glib/gi18n-lib.h>
#	ifdef _WIN32
#		ifdef LOCALEDIR
#			unset LOCALEDIR
#		endif
#		define LOCALEDIR  wpurple_locale_dir()
#	endif
#else
#      define _(a) (a)
#      define N_(a) (a)
#endif

#include <glib.h>
#include "purplecompat.h"
#include "purple2compat/purple-socket.h"

#define EMAIL_PLUGIN_ID "prpl-alyssa-mail"
#ifndef EMAIL_PLUGIN_VERSION
#define EMAIL_PLUGIN_VERSION "0.1"
#endif
#define EMAIL_PLUGIN_WEBSITE "https://notabug.org/alyssa/purple-email"

#define SMTPS_PORT 465
#define IMAPS_PORT 993

enum IMAPState {
	IMAP_ANONYMOUS = 0,
	IMAP_AUTHENTICATING,
	IMAP_AUTHENTICATED,
	IMAP_SELECT,
	IMAP_IDLING,
	IMAP_FETCHING
};

typedef struct {
	PurpleAccount *account;
	PurpleConnection *pc;

	gchar *domain;

	PurpleSocket *imap;
	enum IMAPState imap_state;
	int imap_id;
} EmailAccount;

static gboolean
plugin_load(PurplePlugin *plugin, GError **error)
{
	return TRUE;
}

static gboolean
plugin_unload(PurplePlugin *plugin, GError **error)
{
	purple_signals_disconnect_by_handle(plugin);

	return TRUE;
}

/* Purple2 Plugin Load Functions */
#if !PURPLE_VERSION_CHECK(3, 0, 0)
static gboolean
libpurple2_plugin_load(PurplePlugin *plugin)
{
	return plugin_load(plugin, NULL);
}

static gboolean
libpurple2_plugin_unload(PurplePlugin *plugin)
{
	return plugin_unload(plugin, NULL);
}

static GHashTable *
email_get_account_text_table(PurpleAccount *unused)
{
	GHashTable *table;

	table = g_hash_table_new(g_str_hash, g_str_equal);

	g_hash_table_insert(table, "login_label", (gpointer) _("Email address..."));

	return table;
}

static GList *
email_chat_info(PurpleConnection *pc)
{
	GList *m = NULL;
	PurpleProtocolChatEntry *pce;

	pce = g_new0(PurpleProtocolChatEntry, 1);
	pce->label = _("ID");
	pce->identifier = "id";
	m = g_list_append(m, pce);

	pce = g_new0(PurpleProtocolChatEntry, 1);
	pce->label = _("Name");
	pce->identifier = "name";
	m = g_list_append(m, pce);

	return m;
}

static GHashTable *
email_chat_info_defaults(PurpleConnection *pc, const char *chatname)
{
	GHashTable *defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
	return defaults;
}

#define COMPONENT_USER 0
#define COMPONENT_DOMAIN 1

gchar *
email_address_component(const gchar *address, int component)
{
	gchar **components = g_strsplit(address, "@", 2);
	gchar *out = g_strdup(components[component]);
	g_strfreev(components);
	return out;
}

void
email_imap_login(EmailAccount *acc)
{
	gchar *username = email_address_component(purple_account_get_username(acc->account), COMPONENT_USER);
	gchar *q = g_strdup_printf("a%d LOGIN %s %s\r\n", acc->imap_id++, username, purple_account_get_password(acc->account));
	purple_socket_write(acc->imap, (guchar *) q, strlen(q));
	g_free(q);
	g_free(username);

	acc->imap_state = IMAP_AUTHENTICATING;
}

void email_imap_select(EmailAccount *acc, const char *inbox)
{
	gchar *q = g_strdup_printf("a%d SELECT %s\r\n", acc->imap_id++, inbox);
	purple_socket_write(acc->imap, (guchar *) q, strlen(q));
	g_free(q);
}

void email_imap_idle(EmailAccount *acc)
{
	gchar *q = g_strdup_printf("a%d IDLE\r\n", acc->imap_id++);
	purple_socket_write(acc->imap, (guchar *) q, strlen(q));
	g_free(q);
}

void email_imap_fetch(EmailAccount *acc, const gchar *msg)
{
	gchar *q = g_strdup_printf("a%d FETCH %s (BODY[HEADER] BODY[TEXT])\r\n", acc->imap_id++, msg);
	purple_socket_write(acc->imap, (guchar *) q, strlen(q));
	g_free(q);
}

typedef struct {
	gchar *body;
	gchar *sender;
} IncomingMessage;

gchar *
email_parse_section(gchar *fetch, const gchar *section) {
	gchar *start = g_strrstr(fetch, section);
	
	if(!start) {
		return NULL;
	}

	gchar *end;
	int len = g_ascii_strtoull(start + strlen(section) + 2, &end, 10);
	return g_strndup(end + 3, len - 2);
}

gboolean
email_parse_fetch(gchar *fetch, IncomingMessage *msg)
{
	msg->body = email_parse_section(fetch, "BODY[TEXT]");
	
	if (!msg->body) {
		purple_debug_error(EMAIL_PLUGIN_ID, "No body parsed\n");
		return FALSE;
	}

	gchar *headers = email_parse_section(fetch, "BODY[HEADER]");

	if (!headers) {
		purple_debug_error(EMAIL_PLUGIN_ID, "No headers\n");
		return FALSE;
	}

	GRegex *from_regex = g_regex_new("(^|\r|\n|\r\n)From: (.*)\r\n", 0, 0, NULL);
	GRegex *brackets = g_regex_new("<(.*)>", 0, 0, NULL);
	GMatchInfo *match;

	if (!g_regex_match(from_regex, headers, 0, &match)) {
		purple_debug_error(EMAIL_PLUGIN_ID, "No from\n");
		return FALSE;
	}
	
	gchar *from = g_match_info_fetch(match, 2);
	GMatchInfo *fm;

	if (g_regex_match(brackets, from, 0, &fm)) {
		g_free(from);
		from = g_match_info_fetch(fm, 1);
		g_match_info_free(fm);
	}

	msg->sender = from;

	g_match_info_free(match);
	g_regex_unref(from_regex);
	g_regex_unref(brackets);

	return TRUE;
}

void
email_got_imap_line(EmailAccount *acc, gchar *buffer) {
	if (!buffer || !strlen(buffer))
		return;

	purple_debug_info(EMAIL_PLUGIN_ID, "Line: %s\n", buffer);

	if (acc->imap_state == IMAP_ANONYMOUS) {
		/* Handshake received, login */
		email_imap_login(acc);
	} else if (acc->imap_state == IMAP_AUTHENTICATING) {
		/* Login response received */
		gchar **response = g_strsplit(buffer, " ", 3);
		gchar *code = response[1];

		if (!code) {
			purple_debug_error(EMAIL_PLUGIN_ID, "Uh oh\n%s\n", buffer);
			return;
		}

		if (purple_strequal(code, "OK")) {
			purple_connection_set_state(acc->pc, PURPLE_CONNECTION_CONNECTED);
			acc->imap_state = IMAP_SELECT;
			email_imap_select(acc, "Inbox");
		} else if (purple_strequal(code, "BAD")) {
			purple_connection_error(acc->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Bad username/password"));
			acc->imap_state = IMAP_ANONYMOUS;
		} else {
			/* RIP the spec */
		}

		g_strfreev(response);
	} else if (acc->imap_state == IMAP_SELECT) {
		/* Mailbox selected TODO vet response */

		if (buffer[0] == '*') {
			puts(buffer);
		} else if (buffer[0] == 'a') {
			/* TODO: Is ok? */
			email_imap_idle(acc);
			acc->imap_state = IMAP_IDLING;
		}
	} else if (acc->imap_state == IMAP_IDLING) {
		if (buffer[0] == '*') {
			gchar **words = g_strsplit(buffer, " ", 3);

			if (words[0] && words[1] && words[2] && purple_strequal(words[2], "EXISTS")) {
				gchar *new_message = words[1];

				/* End IDLE to fetch message */
				gchar *q = "DONE\r\n";
				purple_socket_write(acc->imap, (guchar *) q, strlen(q));

				/* and fetch it */
				email_imap_fetch(acc, new_message);
				acc->imap_state = IMAP_FETCHING;
			}

			g_strfreev(words);
		}
	}
}

void
email_got_imap(gpointer user_data, gint fd, PurpleInputCondition condition)
{
	EmailAccount *acc = user_data;
	PurpleSocket *imap = acc->imap;

	guchar buffer[65536];
	gsize sz = purple_socket_read(imap, buffer, sizeof(buffer) - 1);

	if (sz > 0) {
		buffer[sz] = 0;
		
		if (acc->imap_state == IMAP_FETCHING) {
			/* TODO: Parse message correctly :p */
			IncomingMessage msg;

			if (buffer[0] == '*') {
				if (email_parse_fetch((gchar *) buffer, &msg)) {
					purple_serv_got_im(acc->pc, msg.sender, msg.body, PURPLE_MESSAGE_RECV, time(NULL));
					purple_debug_info(EMAIL_PLUGIN_ID, "Incoming: %s from %s\n", msg.body, msg.sender);
					email_imap_idle(acc);
					acc->imap_state = IMAP_IDLING;
				} else {
					purple_debug_error(EMAIL_PLUGIN_ID, "Ruh-roh, couldn't parse %s", (gchar *) buffer);
				}
			}
		} else {
			gchar **lines = g_strsplit((gchar *) buffer, "\r\n", 0);

			for (int i = 0; lines[i]; ++i) {
				email_got_imap_line(acc, lines[i]);
			}

			g_strfreev(lines);
		}
	}
}

void
email_imap_connected(PurpleSocket *ps, const gchar *error, gpointer user_data)
{
	if (error) {
		purple_debug_error(EMAIL_PLUGIN_ID, "Error connecting %s\n", error);
		return;
	}

	purple_debug_info(EMAIL_PLUGIN_ID, "Connected\n");

	purple_socket_watch(ps, PURPLE_INPUT_READ, email_got_imap, user_data);
}

void
email_login(PurpleAccount *account)
{
	EmailAccount *da;
	PurpleConnection *pc = purple_account_get_connection(account);
	PurpleConnectionFlags pc_flags;

	if (!strchr(purple_account_get_username(account), '@')) {
		purple_connection_error(pc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username needs to be an email address"));
		return;
	}

	pc_flags = purple_connection_get_flags(pc);
	pc_flags |= PURPLE_CONNECTION_FLAG_HTML;
	purple_connection_set_flags(pc, pc_flags);

	da = g_new0(EmailAccount, 1);
	purple_connection_set_protocol_data(pc, da);
	da->account = account;
	da->pc = pc;

	const gchar * imap_server = purple_account_get_string(da->account, "imap_server", "");
	if (imap_server && *imap_server) {
		purple_connection_set_state(pc, PURPLE_CONNECTION_CONNECTING);
		/* Do IMAP login */
		purple_debug_info(EMAIL_PLUGIN_ID, "Connecting to IMAP...\n");
		da->imap_state = IMAP_ANONYMOUS;
		da->domain = email_address_component(purple_account_get_username(da->account), COMPONENT_DOMAIN);
		da->imap = purple_socket_new(pc);
		purple_socket_set_tls(da->imap, TRUE);
		purple_socket_set_host(da->imap, imap_server);
		purple_socket_set_port(da->imap, IMAPS_PORT);
		purple_socket_connect(da->imap, email_imap_connected, da);
	} else {
		purple_connection_set_state(pc, PURPLE_CONNECTION_CONNECTED);
	}
}

static GList *
email_status_types(PurpleAccount *account)
{
	GList *types = NULL;
	PurpleStatusType *status;

	status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, "set-online", _("Online"), TRUE, TRUE, FALSE);
	types = g_list_append(types, status);

	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "set-offline", _("Offline"), TRUE, TRUE, FALSE);
	types = g_list_append(types, status);

	return types;
}

static void
email_close(PurpleConnection *pc)
{
	EmailAccount *da = purple_connection_get_protocol_data(pc);
	g_return_if_fail(da != NULL);
	g_free(da);
}

/* Hacky SMTP implementation ¯\_(ツ)_/¯ */

typedef enum {
	SMTP_ANONYMOUS = 0,
	SMTP_HELLO,
	SMTP_AUTH,
	SMTP_FROM,
	SMTP_RCPT,
	SMTP_DATA,
	SMTP_XFER,
	SMTP_QUIT
} SMTPState;

typedef struct {
	EmailAccount *account;
	SMTPState state;
	PurpleSocket *socket;

	/* email addresses + aliases */
	gchar *sender;
	gchar *sender_alias;
	gchar *recipient;
	gchar *recipient_alias;

	gchar *subject;
	gchar *body;
} OutgoingMessage;

void
email_got_smtp(gpointer user_data, gint fd, PurpleInputCondition condition)
{
	OutgoingMessage *msg = user_data;

	gchar buffer[2048];
	gsize sz = purple_socket_read(msg->socket, (guchar*)buffer, sizeof(buffer) - 1);

	if (sz > 0) {
		if (msg->state == SMTP_ANONYMOUS) {
			if (purple_str_has_prefix(buffer, "220")) {
				/* Greetings, server! */
				gchar *ehlo = g_strdup_printf("EHLO %s\r\n", msg->account->domain); /* TODO */
				purple_socket_write(msg->socket, (guchar *) ehlo, strlen(ehlo));
				msg->state++;
			} else {
				goto error;
			}
		} else if (msg->state == SMTP_HELLO) {
			if (purple_str_has_prefix(buffer, "250")) {
				/* Authenticate with plain */
				const gchar *username = purple_account_get_username(msg->account->account);
				const gchar *password = purple_account_get_password(msg->account->account);
				size_t sz = strlen(username) + strlen(password) + 2;
				guchar *buffer = g_malloc(sz);
				buffer[0] = '\0';
				memcpy(buffer + 1, (guchar*) username, strlen(username));
				buffer[1 + strlen(username)] = '\0';
				memcpy(buffer + 1 + strlen(username) + 1, (guchar *) password, strlen(password));
				gchar *base64 = g_base64_encode(buffer, sz);
				g_free(buffer);

				gchar *cmd = g_strdup_printf("AUTH PLAIN %s\r\n", base64);
				purple_socket_write(msg->socket, (guchar *) cmd, strlen(cmd));
				msg->state++;

				g_free(base64);
			} else {
				goto error;
			}
		} else if (msg->state >= (SMTP_FROM - 1) && msg->state <= (SMTP_DATA - 1)) {
			if (buffer[0] == '2') {
				/* Send the next piece of information */
				msg->state++;

				gchar *resp;

				switch (msg->state) {
					case SMTP_FROM:
						resp = g_strdup_printf("MAIL FROM: <%s>\r\n", msg->sender);
						break;
					case SMTP_RCPT:
						resp = g_strdup_printf("RCPT TO: <%s>\r\n", msg->recipient);
						break;
					case SMTP_DATA:
						resp = g_strdup_printf("DATA\r\n");
						break;
					default:
						resp = g_strdup("QUIT");
						break;
				}

				purple_socket_write(msg->socket, (guchar *) resp, strlen(resp));
				g_free(resp);
			} else {
				goto error;
			}
		} else if (msg->state == SMTP_DATA) {
			if (purple_str_has_prefix(buffer, "354")) {
				/* TODO: Escape */
				/* TODO: Date */
				/* TODO: Randomise boundary */

				gchar *boundary = "laescuchdabushdetodoslosescuchdabush";
				gchar *plain = purple_markup_strip_html(msg->body);
				gchar *html = msg->body;
				gchar *subject = NULL;
				if (msg->subject == NULL) {
					char *nlpos = strchr(plain, '\n');
					if (nlpos == NULL) {
						subject = g_strdup(plain);
					} else {
						const size_t subject_len = nlpos - plain;
						subject = g_new0(char, subject_len);
						strncpy(subject, plain, subject_len);
					}
				} else {
					subject = msg->subject; // note: g_strdup happened in caller
				}

				gchar *data = g_strdup_printf(
					"From: %s\r\n"
					"To: \"%s\" <%s>\r\n"
					"Subject: %s\r\n"
					"Content-Type: multipart/alternative; boundary=%s\r\n"
					//"Date: Tue, 15 January 2008 16:02:43 -0500\r\n"
					"\r\n"
					"--%s\r\n"
					"Content-Type: text/plain\r\n"
					"\r\n"
					"%s\r\n"
					"--%s\r\n"
					"Content-Type: text/html\r\n"
					"\r\n"
					"%s\r\n"
					"--%s--\r\n"
					".\r\n",
					msg->sender,
					msg->recipient_alias, msg->recipient,
					subject, boundary,
					boundary, plain,
					boundary, html,
					boundary
				);
				g_free(plain);

				purple_debug_info(EMAIL_PLUGIN_ID, "Sending: „%s“\n", data);
				purple_socket_write(msg->socket, (guchar *) data, strlen(data));
				msg->state++;

				g_free(data);
			} else {
				goto error;
			}
		} else if (msg->state == SMTP_XFER) {
			if (purple_str_has_prefix(buffer, "250")) {
				// display message (happy path)
				PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, msg->recipient, msg->account->account);
				purple_conv_im_write(purple_conversation_get_im_data(conv), msg->sender, msg->body, PURPLE_MESSAGE_SEND, time(NULL));
				
				gchar *quit = "QUIT\r\n";
				purple_socket_write(msg->socket, (guchar *) quit, strlen(quit));
				msg->state++;
			} else {
				goto error;
			}
		} else if (msg->state == SMTP_QUIT) {
			// my gut says, these will not be released in case of error
			g_free(msg->sender);
			g_free(msg->sender_alias);
			g_free(msg->recipient);
			g_free(msg->recipient_alias);
			g_free(msg->subject);
			g_free(msg->body);
			purple_socket_destroy(msg->socket);
			g_free(msg);
		}
		goto not_err;
error:
		// display error
		purple_conv_present_error(msg->recipient, msg->account->account, "Error sending message.");
		purple_debug_error(EMAIL_PLUGIN_ID, "Error!\n");
not_err:
		buffer[sz] = 0;
		puts((gchar *) buffer);
		purple_debug_info(EMAIL_PLUGIN_ID, "(state %d)\n", msg->state);
		return;
	}
}

void
email_smtp_connected(PurpleSocket *ps, const gchar *error, gpointer user_data)
{
	if (error) {
		purple_debug_error(EMAIL_PLUGIN_ID, "Error SMTP connecting %s\n", error);
		return;
	}

	purple_debug_info(EMAIL_PLUGIN_ID, "SMTP Connected\n");

	purple_socket_watch(ps, PURPLE_INPUT_READ, email_got_smtp, user_data);
}

void
email_send_smtp(EmailAccount *account, OutgoingMessage *msg)
{
	msg->socket = purple_socket_new(account->pc);

	purple_socket_set_tls(msg->socket, TRUE);
	purple_socket_set_host(msg->socket, purple_account_get_string(account->account, "smtp_server", account->domain));
	purple_socket_set_port(msg->socket, SMTPS_PORT);
	purple_socket_connect(msg->socket, email_smtp_connected, msg);
}

static int
email_send_im(PurpleConnection *pc,
#if PURPLE_VERSION_CHECK(3, 0, 0)
				PurpleMessage *msg)
{
	const gchar *who = purple_message_get_recipient(msg);
	const gchar *message = purple_message_get_contents(msg);
#else
				const gchar *who, const gchar *message, PurpleMessageFlags flags)
{
#endif

	EmailAccount *da = purple_connection_get_protocol_data(pc);

	OutgoingMessage *outgoing = g_new0(OutgoingMessage, 1);

	outgoing->account = da;
	
	// fill sender (mail From:)
	const gchar *sender = purple_account_get_string(da->account, "mail_from", "");
	if (sender != NULL && sender[0] != '\0') {
		outgoing->sender = g_strdup(sender);
	} else {
		outgoing->sender = g_strdup(purple_account_get_username(da->account));
	}
	
	// fill recipient (mail To:)
	outgoing->recipient = g_strdup(who);
	// friendly name of recipient
	PurpleBuddy *buddy = purple_blist_find_buddy(da->account, who);
	if (buddy && purple_buddy_get_alias(buddy)) {
		outgoing->recipient_alias = g_strdup(purple_buddy_get_alias(buddy));
	} else {
		outgoing->recipient_alias = g_strdup(who);
	}

	// fill subject
	const gchar *subject = purple_account_get_string(da->account, "mail_subject", "");
	if (subject != NULL && subject[0] != '\0') {
		outgoing->subject = g_strdup(subject);
	}
	
	outgoing->body = g_strdup(message);
	outgoing->state = SMTP_ANONYMOUS;

	email_send_smtp(da, outgoing);

	return 0; // do not echo message unless sending succeeds
}


/*
 * This is called after a buddy has been added to the buddy list 
 * (i.e. by manual user interaction).
 */
void
email_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group)
{
	// does not actually do anything, just exists to enable the UI element
}

static GList *
email_add_account_options(GList *account_options)
{
	PurpleAccountOption *option;
	
	option = purple_account_option_string_new(_("SMTP server"), "smtp_server", "");
	account_options = g_list_append(account_options, option);

	option = purple_account_option_string_new(_("IMAP server"), "imap_server", "");
	account_options = g_list_append(account_options, option);

	option = purple_account_option_string_new(_("From"), "mail_from", "");
	account_options = g_list_append(account_options, option);
	
	option = purple_account_option_string_new(_("Subject"), "mail_subject", "");
	account_options = g_list_append(account_options, option);

	return account_options;
}

static const char *
email_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
{
	return "email";
}

static void
plugin_init(PurplePlugin *plugin)
{

#ifdef ENABLE_NLS
	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
#endif

	PurplePluginInfo *info;
	PurplePluginProtocolInfo *prpl_info = g_new0(PurplePluginProtocolInfo, 1);

	info = plugin->info;

	if (info == NULL) {
		plugin->info = info = g_new0(PurplePluginInfo, 1);
	}

	info->extra_info = prpl_info;
#if PURPLE_MINOR_VERSION >= 5
	prpl_info->struct_size = sizeof(PurplePluginProtocolInfo);
#endif

	prpl_info->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_SLASH_COMMANDS_NATIVE | OPT_PROTO_UNIQUE_CHATNAME;
	prpl_info->protocol_options = email_add_account_options(prpl_info->protocol_options);
	prpl_info->icon_spec.format = "png,gif,jpeg";
	prpl_info->icon_spec.min_width = 0;
	prpl_info->icon_spec.min_height = 0;
	prpl_info->icon_spec.max_width = 96;
	prpl_info->icon_spec.max_height = 96;
	prpl_info->icon_spec.max_filesize = 0;
	prpl_info->icon_spec.scale_rules = PURPLE_ICON_SCALE_DISPLAY;

	prpl_info->get_account_text_table = email_get_account_text_table;
	prpl_info->status_types = email_status_types;
	prpl_info->list_icon = email_list_icon;
	prpl_info->chat_info = email_chat_info;
	prpl_info->chat_info_defaults = email_chat_info_defaults;
	prpl_info->login = email_login;
	prpl_info->close = email_close;
	prpl_info->send_im = email_send_im;
	prpl_info->add_buddy = email_add_buddy;
//	prpl_info->join_chat = email_join_chat;
//	prpl_info->get_chat_name = email_get_chat_name;
//	prpl_info->chat_invite = email_chat_invite;
//	prpl_info->chat_send = email_chat_send;
//	prpl_info->set_chat_topic = email_chat_set_topic;

//	prpl_info->roomlist_get_list = email_roomlist_get_list;
//	prpl_info->roomlist_room_serialize = email_roomlist_serialize;
}

static PurplePluginInfo info = {
	PURPLE_PLUGIN_MAGIC,
	/*	PURPLE_MAJOR_VERSION,
		PURPLE_MINOR_VERSION,
	*/
	2, 1,
	PURPLE_PLUGIN_PROTOCOL,			/* type */
	NULL,							/* ui_requirement */
	0,								/* flags */
	NULL,							/* dependencies */
	PURPLE_PRIORITY_DEFAULT,		/* priority */
	EMAIL_PLUGIN_ID,				/* id */
	"Email",						/* name */
	EMAIL_PLUGIN_VERSION,			/* version */
	"",								/* summary */
	"",								/* description */
	"Alyssa Rosenzweig <alyssa@rosenzweig.io>", /* author */
	EMAIL_PLUGIN_WEBSITE,			/* homepage */
	libpurple2_plugin_load,			/* load */
	libpurple2_plugin_unload,		/* unload */
	NULL,							/* destroy */
	NULL,							/* ui_info */
	NULL,							/* extra_info */
	NULL,							/* prefs_info */
	/*email_actions*/ NULL,				/* actions */
	NULL,							/* padding */
	NULL,
	NULL,
	NULL
};

PURPLE_INIT_PLUGIN(email, plugin_init, info);

#else
/* Purple 3 plugin load functions */

G_MODULE_EXPORT GType email_protocol_get_type(void);
#define EMAIL_TYPE_PROTOCOL (email_protocol_get_type())
#define EMAIL_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EMAIL_TYPE_PROTOCOL, EmailProtocol))
#define EMAIL_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EMAIL_TYPE_PROTOCOL, EmailProtocolClass))
#define EMAIL_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMAIL_TYPE_PROTOCOL))
#define EMAIL_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EMAIL_TYPE_PROTOCOL))
#define EMAIL_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EMAIL_TYPE_PROTOCOL, EmailProtocolClass))

typedef struct _EmailProtocol {
	PurpleProtocol parent;
} EmailProtocol;

typedef struct _EmailProtocolClass {
	PurpleProtocolClass parent_class;
} EmailProtocolClass;

static void
email_protocol_init(PurpleProtocol *prpl_info)
{
	PurpleProtocol *info = prpl_info;

	info->id = EMAIL_PLUGIN_ID;
	info->name = "Email";
	info->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_SLASH_COMMANDS_NATIVE | OPT_PROTO_UNIQUE_CHATNAME;
	info->account_options = email_add_account_options(info->account_options);
}

static void
email_protocol_class_init(PurpleProtocolClass *prpl_info)
{
	prpl_info->login = email_login;
	prpl_info->close = email_close;
	prpl_info->list_icon = email_list_icon;
	prpl_info->status_type = email_status_types;
}

static void
email_protocol_im_iface_init(PurpleProtocolIMIface *prpl_info)
{
	prpl_info->send = email_send_im;
}

static void
email_protocol_chat_iface_init(PurpleProtocolChatIface *prpl_info)
{
	prpl_info->send = email_chat_send;
	prpl_info->info = email_chat_info;
	prpl_info->info_defaults = email_chat_info_defaults;
//	prpl_info->join = email_join_chat;
//	prpl_info->get_name = email_get_chat_name;
//	prpl_info->invite = email_chat_invite;
//	prpl_info->set_topic = email_chat_set_topic;
}

static void
email_protocol_server_iface_init(PurpleProtocolServerIface *prpl_info)
{
}

static void
email_protocol_client_iface_init(PurpleProtocolClientIface *prpl_info)
{
	prpl_info->get_account_text_table = email_get_account_text_table;
//	prpl_info->get_actions = email_actions;
//	prpl_info->tooltip_text = email_tooltip_text;
}

static void
email_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *prpl_info)
{
//	prpl_info->add_deny = email_block_user;
//	prpl_info->rem_deny = email_unblock_user;
}

static void
email_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *prpl_info)
{
//	prpl_info->get_list = email_roomlist_get_list;
//	prpl_info->room_serialize = email_roomlist_serialize;
}

static PurpleProtocol *email_protocol;

PURPLE_DEFINE_TYPE_EXTENDED(
	EmailProtocol, email_protocol, PURPLE_TYPE_PROTOCOL, 0,

	PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
									  email_protocol_im_iface_init)

	PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
									  email_protocol_chat_iface_init)

	PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
									  email_protocol_server_iface_init)

	PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
									  email_protocol_client_iface_init)
								  
	PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
									  email_protocol_privacy_iface_init)

	PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
									  email_protocol_roomlist_iface_init)

);

static gboolean
libpurple3_plugin_load(PurplePlugin *plugin, GError **error)
{
	email_protocol_register_type(plugin);
	email_protocol = purple_protocols_add(EMAIL_TYPE_PROTOCOL, error);

	if (!email_protocol) {
		return FALSE;
	}

	return plugin_load(plugin, error);
}

static gboolean
libpurple3_plugin_unload(PurplePlugin *plugin, GError **error)
{
	if (!plugin_unload(plugin, error)) {
		return FALSE;
	}

	if (!purple_protocols_remove(email_protocol, error)) {
		return FALSE;
	}

	return TRUE;
}

static PurplePluginInfo *
plugin_query(GError **error)
{
#ifdef ENABLE_NLS
	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
#endif
	
	return purple_plugin_info_new(
	  "id", EMAIL_PLUGIN_ID,
	  "name", "Email",
	  "version", EMAIL_PLUGIN_VERSION,
	  "category", _("Protocol"),
	  "summary", _("Email Protocol Plugins."),
	  "description", _("Adds Email protocol support to libpurple."),
	  "website", EMAIL_PLUGIN_WEBSITE,
	  "abi-version", PURPLE_ABI_VERSION,
	  "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
				 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
	  NULL);
}

PURPLE_PLUGIN_INIT(email, plugin_query, libpurple3_plugin_load, libpurple3_plugin_unload);

#endif
